//------------------------------------------------------------------------------
//SFWK Framework
//Copyright (C) 2011 SQLI

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;

namespace SFWK.Core
{
    /// <summary>
    /// Provides generic support for context management
    /// </summary>
    public  class ContextHelper
    {

        #region Public methods
        /// <summary>
        /// Returns either Web Request Data or a Thread context data if not running a Web request (ie. HttpContext.Current)
        /// is not available.
        /// </summary>
        /// <typeparam name="TDataContext"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T GetFromContext<T>(object key)
        {
            if (HttpContext.Current != null)
                return GetWebRequestContext<T>(key);

            return GetThreadScopedContext<T>(key);
        }

        /// <summary>
        /// Sets to either Web Request Data or a Thread context data if not running a Web request (ie. HttpContext.Current)
        /// is not available.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        public static void SetToContext<T>(object key, T obj)
        {
            if (HttpContext.Current != null)
                SetWebRequestContext<T>(key, obj);

            SetThreadScopedContext<T>(key, obj);
        }


        #endregion

        #region Private Methods


        /// <summary>
        /// Internal method that handles creating a context that is scoped to the HttpContext Items collection
        /// by creating and holding the DataContext there.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="key"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        static T GetWebRequestContext<T>(object key)
        {
            return (T)HttpContext.Current.Items[_getHttpContextKey(key)];
        }

        static void SetWebRequestContext<T>(object key, T obj)
        {
            if (HttpContext.Current.Items[_getHttpContextKey(key)] == null)
                HttpContext.Current.Items.Add(_getHttpContextKey(key), obj);
            HttpContext.Current.Items[_getHttpContextKey(key)] = obj;
        }

        /// <summary>
        /// Creates a Thread Scoped DataContext object that can be reused.
        /// The DataContext is stored in Thread local storage.
        /// </summary>
        /// <typeparam name="TDataContext"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        static T GetThreadScopedContext<T>(object key)
        {
            LocalDataStoreSlot dataStoreSlot = Thread.GetNamedDataSlot(_getThreadContextKey(key));
            return (T)Thread.GetData(dataStoreSlot);
        }

        static void SetThreadScopedContext<T>(object key, T obj)
        {
            LocalDataStoreSlot dataStoreSlot = Thread.GetNamedDataSlot(_getThreadContextKey(key));
            if (dataStoreSlot == null)
                dataStoreSlot = Thread.AllocateNamedDataSlot(_getThreadContextKey(key));
            Thread.SetData(dataStoreSlot, obj);
        }



        /// <summary>
        /// Operator Key to thread context
        /// </summary>
        private static string _getThreadContextKey(object key)
        {
            return String.Format("{0}_{1}", "__" + key.ToString() + "_", Thread.CurrentContext.ContextID.ToString());
        }

        /// <summary>
        /// Operator Key to http context
        /// </summary>
        private static string _getHttpContextKey(object key)
        {
            return "__" + key.ToString() + "_" + HttpContext.Current.GetHashCode().ToString("x") + Thread.CurrentContext.ContextID.ToString();

        }
        #endregion

    }
}

